/*
 * @Author: youngqj youngqj@126.com
 * @Date: 2024-11-01 23:25:11
 * @LastEditors: youngqj youngqj@126.com
 * @LastEditTime: 2024-12-04 19:51:51
 * @FilePath: /mallSystem/app/handler/auth_controller.go
 * @Description:
 *
 * Copyright © Zhejiang Xiaoqu Information Technology Co., Ltd, All Rights Reserved.
 */
package handler

import (
	"context"
	"fmt"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"golang.org/x/crypto/bcrypt"

	"xiaoquio/web/core/config"
	"xiaoquio/web/core/utils"
	"xiaoquio/web/middleware"
	"xiaoquio/web/model"
)

/*
* 用户注册接口
 */
func Register(c *gin.Context) {
	//从上下文中获取接口

	db, err := middleware.GetDB(c, "default")
	if err != nil {

		return
	}

	var formData struct {
		Username string `json:"username" binding:"required,min=4,max=16"`
		Passwd   string `json:"passwd" binding:"required,min=8,max=128"`
		Nikname  string `json:"nikname" binding:"max=200"` // 限制昵称长度
		Avatar   string `json:"avatar" binding:"max=255"`  // 限制头像 URL 长度
	}

	if err := c.ShouldBindJSON(&formData); err != nil {
		utils.ResponseNilData(c, 200, "数据格式错误！")
		return
	}
	//检查唯一性

	var existsMember model.MemberModel
	if err := db.Where("username = ?", formData.Username).First(&existsMember).Error; err == nil {
		utils.ResponseNilData(c, 200, "用户名已经存在啦！")
		return
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(formData.Passwd), bcrypt.DefaultCost)
	if err != nil {
		fmt.Println("密码加密失败")
		utils.ResponseNilData(c, 200, "密码加密失败")
		return
	}
	member := model.MemberModel{
		Username: formData.Username,
		Passwd:   string(hashedPassword),
		Nikname:  formData.Nikname,
		Avatar:   formData.Avatar,
		CreateAt: time.Now().Unix(),
		IsValid:  true,
	}
	if err := db.Create(&member).Error; err != nil {
		utils.ResponseNilData(c, 200, "注册失败")
		return
	}
	utils.ResponseWithData(c, member, "注册成功！")

}

/*
用户登录
*/
func Login(c *gin.Context) {
	db, err := middleware.GetDB(c, "default")
	if err != nil {

		return
	}

	var formData struct {
		Username string `json:"username" binding:"required,min=4,max=16"`
		Passwd   string `json:"passwd" binding:"required,min=8,max=128"`
		DeviceID string `json:"device_id" binding:"required,min=8,max=128"`
	}

	if err := c.ShouldBindJSON(&formData); err != nil {
		utils.ResponseNilData(c, 200, "数据格式错误！")
		return
	}

	//查找用户是否存在
	var member model.MemberModel
	if err := db.Where("username = ? ", formData.Username).First(&member).Error; err != nil {
		utils.ResponseNilData(c, 200, "用户不存在或者密码错误")
		return
	}
	if !member.IsValid {
		utils.ResponseNilData(c, 200, "账户已经被锁定请联系管理员")
		return
	}
	// 验证密码
	if err := bcrypt.CompareHashAndPassword([]byte(member.Passwd), []byte(formData.Passwd)); err != nil {
		utils.ResponseNilData(c, 200, "密码错误")
		return
	}
	siteConfig, _ := config.LoadConfig("../config/config.yaml")
	jwtKey := siteConfig.GetString("siteConfig.auth_jwt_key")
	user := fmt.Sprintf("%v", member.ID)
	userID, _ := utils.Encrypt(user)
	accessToken, err := utils.GenerateToken(userID, formData.DeviceID, jwtKey)
	if err != nil {
		utils.ResponseNilData(c, 200, "生成token 失败了")
		return
	}
	refreshToken, _ := utils.GenerateRefreshToken(formData.DeviceID) //生成设备ID对应的刷新token 令牌

	retData := map[string]interface{}{
		"access_token":  accessToken,
		"expire_time":   7200,
		"device_id":     formData.DeviceID,
		"user_id":       userID,
		"refresh_token": refreshToken,
	}
	rds, err := middleware.GetRedis(c)
	if err != nil {
		utils.ResponseNilData(c, 500, "无法连接到 Redis")
		return
	}
	ctx := context.Background()
	tokenKey := fmt.Sprintf("user:%s:access_token", userID)
	storeTokenRet := storeTokenToRedis(ctx, rds, tokenKey, accessToken, time.Duration(7200)*time.Second)
	if !storeTokenRet {
		utils.ResponseNilData(c, 500, "更新 Redis AccessToken 失败")
		c.Abort()
		return
	}

	refrehTokenKey := fmt.Sprintf("user:%s:refreh_token", userID)

	storeRefreshTRet := storeTokenToRedis(ctx, rds, refrehTokenKey, refreshToken, time.Duration(96)*time.Hour)
	if !storeRefreshTRet {
		utils.ResponseNilData(c, 500, "更新 Redis refreh_token 失败")
		c.Abort()
		return
	}

	utils.ResponseWithData(c, retData, "登录成功")
	//utils.RetSucess(c, "登录 成功！")

}

func storeTokenToRedis(ctx context.Context, rds *redis.Client, tokenKey string, accessToken string, ttl time.Duration) bool {
	existsKey, err := rds.Exists(ctx, tokenKey).Result()
	if err != nil {
		//fmt.Println(11111)
		return false
	}
	// 如果 tokenKey 已存在，则删除旧的 token
	if existsKey > 0 { // 如果 existsKey 大于 0，表示键存在
		err := rds.Del(ctx, tokenKey).Err()
		if err != nil {
			fmt.Println(2222)
			return false
		}
	}

	err = rds.Set(ctx, tokenKey, accessToken, ttl).Err()
	//fmt.Println(err == nil)
	return err == nil
}
